package transclusion;

import processing.core.*;
import transclusion.items.*;

import java.util.*;

public class DAG {
	String root;
	String[] lines;
	public boolean isSourceBased;
	public PApplet papplet;
	// could be source vs targets (if isSourceBased==true)
	// or target vs sources (if isSourceBased==false)
	public HashMap<Article, ArrayList<Article>> transcludedArticles;
	public HashMap<Category, ArrayList<Article>> categoryArticles;
	public HashMap<Category, ArrayList<Category>> categoryDAG;
	
	public DAG(PApplet p, String r) {
		this(p,r,true);
	}
	
	public DAG(PApplet p, String r, boolean srcBased) {
		papplet = p;
		root = r;
		isSourceBased = srcBased;
		createTables();
	}
	
	public String root() {
		return root;
	}
	
	public Category rootCategory() {
		return new Category(this, root);
	}
	
	public void setRoot(String r) {
		root = r;
	}
	
	protected void createTables() {
		transcludedArticles = new HashMap<Article, ArrayList<Article>>();
		categoryArticles = new HashMap<Category, ArrayList<Article>>();
		categoryDAG = new HashMap<Category, ArrayList<Category>>();		
		lines = papplet.loadStrings("targets.csv");		
		if(isSourceBased) {
			for (int i = 0; i < lines.length; i++) {
				String[] pieces = splitLine(lines[i]);
				Article source = null;
				ArrayList<Article> targets = new ArrayList<Article>();
				for (int j = 0; j < pieces.length; j++) {
					if(j == 0)
						source = new Article(this, pieces[j]);
					if(j > 0)
						if(!targets.contains(new Article(this, pieces[j], source)))
							targets.add(new Article(this, pieces[j], source));
				}
				transcludedArticles.put(source, targets);
			}
		}
		else {
			for (int i = 0; i < lines.length; i++) {
				String[] pieces = splitLine(lines[i]);
				Article source = null;
				Article target = null;
				for (int j = 0; j < pieces.length; j++) {
					if(j == 0)
						source = new Article(this, pieces[j]);
					if(j > 0) {					
						target = new Article(this, pieces[j]);
						ArrayList<Article> sources = transcludedArticles.get(target); 
						if (sources == null)
							sources = new ArrayList<Article>();
						if( !sources.contains(source) )
							sources.add(source);
						transcludedArticles.put(target, sources);					
					}
				}				
			}
		}
		// debug
		//printArticleTargets(sourceTargets);
		
		if( isSourceBased )
			lines = papplet.loadStrings("sources/parents.csv");
		else
			lines = papplet.loadStrings("targets/parents.csv");
		for (int i = 0; i < lines.length; i++) {
			String[] pieces = splitLine(lines[i]);			
			Category category = null;			
			Article article = null;
			for (int j = 0; j < pieces.length; j++) {				
				if(j == 0)
					article = new Article(this, pieces[j], (pieces.length - 1));
				if(j > 0) {					
					category = new Category(this, pieces[j]);
					ArrayList<Article> articleList = categoryArticles.get(category); 
					if (articleList == null)
						articleList = new ArrayList<Article>();
					if( !articleList.contains(article) )
						articleList.add(article);
					categoryArticles.put(category, articleList);					
				}
			}
		}
		// debug
		//printCategoryArticles(categorySources);
				
		if( isSourceBased )
			lines = papplet.loadStrings("sources/dag.csv");
		else
			lines = papplet.loadStrings("targets/dag.csv");
		for (int i = 0; i < lines.length; i++) {
			String[] pieces = splitLine(lines[i]);			
			Category categoryParent = null;
			Category categoryChild = null;
			for (int j = 0; j < pieces.length; j++) {				
				if(j == 0)
					categoryChild = new Category(this, pieces[j]);
				if(j > 0) {					
					categoryParent = new Category(this, pieces[j]);
					ArrayList<Category> categoryList = categoryDAG.get(categoryParent); 
					if ( categoryList == null )
						categoryList = new ArrayList<Category>();
					if( !categoryList.contains(categoryChild) ) {
						categoryList.add(categoryChild);
						categoryChild.setParent(categoryParent);
					}
					categoryDAG.put(categoryParent, categoryList);				
				}
			}			
		}
		// debug
		//printParentCategories(categoryChildren);
	}
	
	void printArticleTargets(HashMap<Article, ArrayList<Article>> map) {		
		PApplet.println("Sources vs targets (keys and values)");
		for (Map.Entry<Article, ArrayList<Article>> entry : map.entrySet()) {
		    Article key = entry.getKey();
		    ArrayList<Article> value = entry.getValue();
		    PApplet.print(" key " + key.name() + " value:");
		    for(Article article : value)
				PApplet.print(" " + article.name());
		}		
		PApplet.println();		
	}
	
	void printCategoryArticles(HashMap<Category, ArrayList<Article>> map) {	
		PApplet.println("Category vs Source articles (keys and values)");
		for (Map.Entry<Category, ArrayList<Article>> entry : map.entrySet()) {
		    Category key = entry.getKey();
		    ArrayList<Article> value = entry.getValue();
		    PApplet.print(" key " + key.name() + " value:");
		    for(Article article : value)
				PApplet.print(" " + article.name());
		}
		PApplet.println();	
	}
	
	void printParentCategories(HashMap<Category, ArrayList<Category>> map) {	
		PApplet.println("Parent vs children categories (keys and values)");
		for (Map.Entry<Category, ArrayList<Category>> entry : map.entrySet()) {
		    Category key = entry.getKey();
		    ArrayList<Category> value = entry.getValue();
		    PApplet.print(" key " + key.name() + " value:");
		    for(Category categoryChild : value)
				PApplet.print(" " + categoryChild.name());
		}
		PApplet.println();
	}

	String[] splitLine(String line) {
		char[] c = line.toCharArray();
		ArrayList pieces = new ArrayList();
		int prev = 0;
		boolean insideQuote = false;
		for (int i = 0; i < c.length; i++) {
			if (c[i] == ',') {
				if (!insideQuote) {
					// Whitespace must be trimmed between commas.
					String s = new String(c, prev, i - prev).trim();
					pieces.add(s);
					prev = i + 1;
				}
			} else if (c[i] == '\"') {
				insideQuote = !insideQuote;
			}
		}
		if (prev != c.length) {
			String s = new String(c, prev, c.length - prev).trim();
			pieces.add(s);
		}
		String[] outgoing = new String[pieces.size()];
		pieces.toArray(outgoing);
		scrubQuotes(outgoing);
		return outgoing;
	}

	// Parse quotes from CSV data. Quotes around a column are common,
	// and actual double quotes (") are specified by two double quotes ("").
	void scrubQuotes(String[] array) {
		for (int i = 0; i < array.length; i++) {
			if (array[i].length() > 2) {
				// Remove quotes at start and end, if present.
				if (array[i].startsWith("\"") && array[i].endsWith("\"")) {
					array[i] = array[i].substring(1, array[i].length() - 1);
				}
			}
			// Make double quotes into single quotes.
			array[i] = array[i].replaceAll("\"\"", "\"");
		}
	}
	
	public boolean isInteger( String input ) {
		try {
			Integer.parseInt( input );
			return true;
		} 
		catch( NumberFormatException ex ) {
			return false;
		}
	}
}